package com.base.engine;

import java.awt.Dimension;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.util.WaveData;

import com.base.engine.error.EMADErrorCode;
import com.base.engine.error.MADErrorHandler;
import com.base.engine.font.MADFontManager;
import com.base.engine.game.MADGame;
import com.base.engine.graphics.Color4f;
import com.base.engine.graphics.EDisplayMode;
import com.base.engine.graphics.MADBasicGL;
import com.base.engine.graphics.MADRenderer;
import com.base.engine.graphics.VertexArray2f;
import com.base.engine.gui.MADGamingInterface;
import com.base.engine.gui.MADUserInterface;
import com.base.engine.input.MADInputHandler;
import com.base.engine.sound.MADBasicAL;

import static org.lwjgl.opengl.GL11.* ;
import static org.lwjgl.openal.AL10.* ; 

public final class MADEngine {

	public static final int WIDTH = (int)getScreenRes().getWidth()   ; 
	public static final int HEIGHT = (int)getScreenRes().getHeight()  ; 
	public static final int LEFT = 0 ; 
	public static final int TOP = 0 ; 
//	public static final int WIDTH = 1350 ; 
//	public static final int HEIGHT = 750 ; 
//	public static final int LEFT = 1680 ; 
//	public static final int TOP = 220 ; 
	
	public static IntBuffer selectionBuffer; 
	public static IntBuffer viewport ; 
	
	public static final VertexArray2f displayVertices = MADRenderer.toRectangle_2f(0, 0, WIDTH, HEIGHT, 
																MADGamingInterface.u, MADGamingInterface.v, 
																new Color4f(0,0,0,0), EDisplayMode.DM_COLORED, 
																"screenCoordinates"); 
	
	private static MADGame game ; 
	private static boolean fullscreen ; 
	public static String appTitle = "MADEngine"; 
	
	public static void create(MADGame g, boolean fullscreen){
		setGame(g);
		setFullscreen(fullscreen); 
		initDisplay(); 
		initGL(); 
//		initAL(); 
		initComponents();
		initGame();
		gameLoop(); 
		exit() ; 
	}

	public static MADGame getGame() {
		return game;
	}

	public static void setGame(MADGame g) {
		if(g == null) MADErrorHandler.throwException(EMADErrorCode.GAME_REF, new NullPointerException("Invalid game - Game reference is NULL!"));
		else{
			game = g ;
			appTitle = g.getTitle(); 
		}
			
	}
	
	public static boolean isFullscreen() {
		return fullscreen;
	}

	public static void setFullscreen(boolean fullscreen) {
		MADEngine.fullscreen = fullscreen;
		if(Display.isCreated())
			try {
				Display.setFullscreen(fullscreen);
			} catch (LWJGLException e) {
				MADErrorHandler.throwException(EMADErrorCode.DISPLAY_MODE, e); 
			}
	}
	
	/* --- Setting up the Display && Rendering content --- */ 	
	private static DisplayMode getDisplayMode(int targetWidth, int targetHeight){
		try {
			DisplayMode[] modes = Display.getAvailableDisplayModes();
			for (DisplayMode m : modes) {
				if (m.isFullscreenCapable() && m.getWidth() == targetWidth && m.getHeight() == targetHeight)
					return m;
			}
		} catch (Exception e) {
			MADErrorHandler.throwException(EMADErrorCode.DISPLAY_MODE, e); 
		}
		return null;
	}
	
	private static void initDisplay(){
		try{
			DisplayMode displayMode = getDisplayMode(WIDTH, HEIGHT); 
			if(!(displayMode == null))
				Display.setDisplayMode(displayMode); 
			else
				Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT)); 
			Display.setFullscreen(fullscreen);
			Display.setVSyncEnabled(true);
//			Display.setResizable(true);
			Display.setLocation(LEFT,TOP);
			Display.setTitle(game.getTitle());
			Display.create(); 
			
			MADFrameRate.start(); 
		}catch(LWJGLException e){
			MADErrorHandler.throwException(EMADErrorCode.CREATE_DISPLAY, e); 
		}
	}
	
	private static void initGL(){
		try{
			MADBasicGL.initGL();
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.INIT_GL, e);
		}
	}
	
	private static void exitGL(){
		try{
			MADBasicGL.exitGL();
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.EXIT_GL, e);
		}
	}
	
	private static void initAL(){
		try{
			MADBasicAL.initAL() ; 
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.INIT_AL, e); 
		}
	}
	
	private static void exitAL(){
		try{
			MADBasicAL.exitAL() ; 
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.EXIT_AL, e) ; 
		}
	}
	
	public static void exit(){
		try{
			exitGL(); 
//			exitAL(); 
			game = null ; 
			Mouse.destroy(); 
			Keyboard.destroy();
			Display.destroy(); 
			destroy(); 
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.EXIT_APPLICATION, e);
		}
	}
	public static void destroy(){
		System.exit(0);
	}
	
	/* --- Initializing other components ( e.g. TextRenderer ... ) ---*/ 
	private static void initComponents(){
		try{
			MADUserInterface.create(game);
			MADFontManager.init(); 
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.INIT_COMPONENTS, e) ; 
		}
	}
	
	/* --- Initializing the actual game --- */ 
	private static void initGame(){
		try{	
			game.init(); 
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.INIT_GAME, e) ; 
		}
	}
	
	/* --- The basic game loop --- */ 
	private static void gameLoop(){
		try{
			selectionBuffer = BufferUtils.createIntBuffer(256);
			glSelectBuffer(selectionBuffer);
			viewport = BufferUtils.createIntBuffer(16); 
			glViewport(0,0,WIDTH,HEIGHT); 
			glGetInteger(GL_VIEWPORT,viewport); 
			while(!Display.isCloseRequested()){
				getInput(); 
				update(); 
				render(); 
//				alSourcePlay(source); 
				Display.sync(60);
				Display.update(); 
			}
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.GAME_LOOP, e) ; 
		}
	}
	
	/* --- Keeping the running game alive ---- */ 
	private static void update(){
		try{
			MADUserInterface.update();
			game.update(); 
			MADFrameRate.update();
			MADMemory.update(); 
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.GENERAL_UPDATE, e) ; 
		}
	}
	
	private static void render(){
		try{
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
			glLoadIdentity();
			
			glEnable(GL_LINE_SMOOTH);
	
			glPushMatrix(); 
				game.render();
				MADUserInterface.render();
			glPopMatrix(); 

			glDisable(GL_LIGHTING);
			glDisable(GL_LIGHT1);
			glDisable(GL_COLOR_MATERIAL);
			glDisable(GL_LINE_SMOOTH);
			 
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.GENERAL_RENDERING, e) ; 
		}
	}
	
	private static void getInput(){
		try{ 
			MADInputHandler.getKeyboardInput(game);
			if(!MADUserInterface.getMenuScreen().isVisible())
				game.getInput(); 
			else
				MADInputHandler.getMouseInput(game);
		}catch(Exception e){
			MADErrorHandler.throwException(EMADErrorCode.GENERAL_INPUT, e) ; 
		}
	}
	
	/* --- Getting the screen resolution --- */ 
	private static Dimension getScreenRes(){
		return java.awt.Toolkit.getDefaultToolkit().getScreenSize();
	}
	
}
